home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gekkan Dennou Club 147
/
Gekkan Dennou Club - 2000.8 Vol. 147 (Japan).7z
/
Gekkan Dennou Club - 2000.8 Vol. 147 (Japan) (Track 1).bin
/
tools
/
zmc3v078
/
zmc3v078.lzh
/
SRCSV078.LZH
/
V3MACRO.C
< prev
next >
Wrap
C/C++ Source or Header
|
2000-06-01
|
19KB
|
721 lines
/* ==========================
V3マクロ展開
ここだけ当面日本語注釈
高速化は後回し アルゴリズム低級
.include もここで処理する
========================== */
/*#define DEBUG*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "parsesub.h"
#include "etc.h"
#include "68lib.h"
#include "switch.h"
#include "structs2.h"
#ifndef NOINCLUDE
#include "readfile.h"
#endif
UBYTE *expandV3macro(UBYTE *zms, LINEDATA *ld, LINEFILEDATA *lfd);
/* 行毎にデータを分割するための構造 */
typedef struct zmsData {
struct zmsData *next;
UBYTE *linebuf; /* */
DWORD line; /* 行番号 */
} ZMSDATA;
typedef struct v3macropara {
struct v3macropara *next;
char *para;
} V3MACROPARA;
/* マクロ記憶 */
typedef struct v3macro {
struct v3macro *next;
UBYTE *state1; /* 展開前 */
UBYTE *state2; /* 展開後 */
DWORD paras; /* パラメータの数 */
/* struct v3macropara *para; */
} V3MACRO;
V3MACRO *expandMacroMain(ZMSDATA *zmsdata, LINEDATA *ld, LINEFILEDATA *lfd);
ZMSDATA *defineMacroMain(ZMSDATA *zmsdata, char **lbuf__, V3MACRO *mbuf, LINEDATA *ld);
void replaceMacro(ZMSDATA *zmsdata, V3MACRO *mbuf);
ZMSDATA *makeLineBuf(UBYTE *zmsbuf, LINEDATA *ld, LINEFILEDATA *lfd);
UBYTE *restoreLineBuf(ZMSDATA *zmsdata);
char *skipSpc2(char *lbuf);
char *skipSpc3(char *lbuf, ZMSDATA **zmsdata);
UBYTE *getpara2(UBYTE *lbuf, char **para);
#ifndef NOINCLUDE
void includeFile(ZMSDATA *zmsdata, char *filename, LINEDATA *ld, LINEFILEDATA *lfd);
#endif
void freeV3macro(V3MACRO *macro) {
while (macro->next != NULL) {
V3MACRO *before = macro;
efree(macro->state1,"mbuf->state1");
efree(macro->state2,"mbuf->state2");
macro = macro->next;
efree(before,"before");
}
efree(macro,"macro");
}
#ifndef NOINCLUDE
void includeFile(ZMSDATA *zmsdata, char *filename, LINEDATA *ld, LINEFILEDATA *lfd)
{
ZMSDATA *zmsdata2;
ZMSDATA *orgnext = zmsdata->next;
ZMSDATA *z;
UBYTE *buf;
const char *inpEXT[] = { ".zmc", NULL};
LINEDATA *addld, *bakld, *l;
buf = readFile1(filename, strlen(filename), inpEXT, 0);
lfd->next = (LINEFILEDATA*)emalloc(sizeof(LINEFILEDATA) * 1,"linefile_in_includeFile");
lfd = lfd->next;
lfd->next = NULL;
lfd->filename = (char*)emalloc(sizeof(char) * (strlen(filename) + 1), "filename_in_includeFile");
strcpy(lfd->filename, filename);
addld = (LINEDATA*)emalloc(sizeof(LINEDATA) * 1,"line_in_includeFile");
zmsdata2 = makeLineBuf(buf, addld, lfd);
efree(buf,"buf_in_includeFile");
zmsdata->next = zmsdata2;
z = zmsdata2;
while (zmsdata2->next) {
z = zmsdata2;
zmsdata2 = zmsdata2->next;
}
z->next = orgnext;
efree(zmsdata2,"zmsdata2"); /* add v0.70 */
bakld = ld->next;
l = addld;
ld->next = addld;
while (ld->next) {
l = ld;
ld = ld->next;
}
l->next = bakld;
efree(ld,"l");
}
#endif /* #ifndef NOINCLUDE */
UBYTE *expandV3macro(UBYTE *zms, LINEDATA *ld, LINEFILEDATA *lfd)
{
ZMSDATA *zmsdata;
UBYTE *zmsbuf;
/*int i;*/
zmsdata = makeLineBuf(zms,ld, lfd); /* まず行毎にデータ分割 */
/*
for (i = 1; i < 3; i++) {
ld->next = (LINEDATA*)emalloc(sizeof(LINEDATA) * 1,"linedata_next_in_makeLineBuf");
ld->next->line = ld->line;
ld = ld->next;
ld->next = NULL;
}
*/
freeV3macro(expandMacroMain(zmsdata,ld,lfd));
efree(zms,"zms_in_v3macro"); /* もう不要 */
#ifdef AA
/* 表示テスト */
fprintf(stderr,"=============================\n");
zmsdata2 = zmsdata;
while (zmsdata2->next) {
ZMSDATA *before = zmsdata2;
UBYTE *buf = zmsdata2->linebuf;
fprintf(stderr,"%s",zmsdata2->linebuf);
zmsdata2 = zmsdata2->next;
/*
efree(buf);
efree(before);
*/
}
#endif
zmsbuf = restoreLineBuf(zmsdata);
return zmsbuf;
}
/* ============================================
テキストをスキャンし、もしマクロがあれば
定義する
============================================ */
V3MACRO *expandMacroMain(ZMSDATA *zmsdata, LINEDATA *ld, LINEFILEDATA *lfd)
{
V3MACRO *mbuf;
int defMacroFlag = 1;
mbuf = (V3MACRO*)emalloc(sizeof(V3MACRO) * 1,"mbuf_in_expandMacroMain");
mbuf->next = NULL;
while (zmsdata->next != NULL) {
char *lbuf;
replaceMacro(zmsdata, mbuf);
lbuf = skipSpc2(zmsdata->linebuf);
if (*lbuf == '.') {
if (!stricmp2(lbuf, ".define")) { /* マクロ定義が存在 */
/* if (defMacroFlag) { */
zmsdata = defineMacroMain(zmsdata, &lbuf, mbuf, ld);
/* } */
if (!defMacroFlag) { /* 手抜き:仮対策 */
strcpy(zmsdata->linebuf, "\n"); /* erase define line */
}
#ifndef NOINCLUDE
} else if (!stricmp2(lbuf,".include")) {
char filename[1024];
int p = 0;
lbuf = skipSpc2(lbuf + 8);
while (isgraph(*lbuf) && *lbuf != '/') {
filename[p++] = *lbuf++;
}
filename[p] = '\0';
includeFile(zmsdata, filename, ld, lfd);
#endif /* #ifndef NOINCLUDE */
} else if(!stricmp2(lbuf, ".track")) {
defMacroFlag = 0;
}
} else if (*lbuf == '(') {
lbuf = skipSpc2(lbuf);
if (*lbuf == 't' || *lbuf == 'T') { /* (t..) 開始(共通トラック部終了) */
defMacroFlag = 0;
/* break; */ /* マクロスキャン打ち切り */
}
}
zmsdata = zmsdata->next;
ld = ld->next;
}
return mbuf;
}
/* ベタのデータを行毎に分ける */
ZMSDATA *makeLineBuf(UBYTE *zms, LINEDATA *ld, LINEFILEDATA *lfd)
{
ZMSDATA *zmsdata, *ret;
DWORD line = 1;
ret = zmsdata = (ZMSDATA*)emalloc(sizeof(ZMSDATA) * 1,"zmsdata_in_makeLineBuf");
zmsdata->next = NULL;
zms--;
while (*++zms != 0xFF) {
UBYTE *before = zms;
/* 1 line scan */
while (*zms != 0x0A && *zms != 0xFF) {
zms++;
}
/* copy line */
zmsdata->linebuf = (UBYTE*)emalloc(sizeof(UBYTE) * (zms - before + 2),"linebuf_in_makeLineBuf");
strncpy(zmsdata->linebuf, before, zms - before + 1);
zmsdata->linebuf[zms - before + 1] = '\0';
zmsdata->line = line;
ld->line = line++;
ld->filename = lfd->filename;
/* malloc next list */
zmsdata->next = (ZMSDATA*)emalloc(sizeof(ZMSDATA) * 1,"next_in_makeLineBuf");
zmsdata = zmsdata->next;
zmsdata->next = NULL;
ld->next = (LINEDATA*)emalloc(sizeof(LINEDATA) * 1,"linedata_next_in_makeLineBuf");
ld = ld->next;
ld->next = NULL;
}
return ret;
}
#ifndef MSDOS
#define ZMSBUF_SIZE (128 * 1024)
#define ZMSBUF_EXPANDSIZE (64 * 1024)
#else
#define ZMSBUF_SIZE (16 * 1024)
#define ZMSBUF_EXPANDSIZE (4 * 1024)
#endif
/* 行単位のデータを元のベタデータに戻す */
UBYTE *restoreLineBuf(ZMSDATA *zmsdata)
{
UBYTE *zmsbuf2, *zpos;
DWORD zmssize = ZMSBUF_SIZE;
#ifdef AA
fprintf(stderr,"----------------\n");
#endif
zpos = zmsbuf2 = (UBYTE*)emalloc(sizeof(UBYTE) * zmssize,"zmsbuf2");
while (zmsdata->next != NULL) { /* 元のベタデータとして連結 */
ZMSDATA *before = zmsdata;
DWORD len;
len = strlen(zmsdata->linebuf);
/* ここで拡張すればメモリリークの心配はないはず */
if (len + 1 + (zpos - zmsbuf2) > zmssize) {
/* メモリ拡張のオーバーヘッド回避のため多めに確保 */
DWORD p = zpos - zmsbuf2;
zmssize += len + 1 + ZMSBUF_EXPANDSIZE;
zmsbuf2 = (UBYTE*)erealloc(zmsbuf2, zmssize,"zmsbuf2_1");
zpos = zmsbuf2 + p;
}
strcpy(zpos, zmsdata->linebuf);
zpos += len;
zmsdata = zmsdata->next;
efree(before->linebuf,"before->linebuf");
efree(before,"before");
}
efree(zmsdata,"zmsdata");
*zpos++ = '\xFF';
*zpos++ = '\xFF';
*zpos++ = '\xFF';
*zpos++ = '\xFF';
*zpos++ = '\xFF';
*zpos++ = '\xFF';
zmsbuf2 = erealloc(zmsbuf2, zpos - zmsbuf2 + 1,"zmsbuf2_2");
if (getSwitchVal('d') != RES_VAL) {
FILE *fp = fopen("debug.zms","wb");
if (fp == (FILE*)NULL) {
fatal(1,"can't open debug.zms.\n");
}
fwrite(zmsbuf2,sizeof(UBYTE),zpos-zmsbuf2+1 - 7,fp);
fclose(fp);
}
return zmsbuf2;
}
char *skipSpc2(char *lbuf)
{
while ((*lbuf == ' ' || *lbuf == '\t') && *lbuf) {
lbuf++;
}
if (*lbuf == '/' ) {
while (*lbuf != 0x0A) {
lbuf++;
}
}
return lbuf;
}
char *skipSpc3(char *lbuf, ZMSDATA **zmsdata)
{
int f;
do {
f = 0;
while (*lbuf && (*lbuf == ' ' || *lbuf == '\t')) {
lbuf++;
}
if (*lbuf == '/' ) {
while (*lbuf) {
lbuf++;
}
}
if (!*lbuf) {
*zmsdata = (*zmsdata)->next;
lbuf = (*zmsdata)->linebuf;
if ((*zmsdata)->next) {
f = 1;
} else {
zmserror("macro definition is not closed.",(*zmsdata)->line,(*zmsdata)->linebuf,lbuf,0,1);
}
}
} while (f);
return lbuf;
}
/* ====================
マクロ定義メイン
==================== */
ZMSDATA *defineMacroMain(ZMSDATA *zmsdata, char **lbuf__, V3MACRO *mbuf, LINEDATA *ld)
{
char *lbuf = *lbuf__ + 7, *lbuf_;
ZMSDATA *zmsdata_ = zmsdata;
int len, paras = 0, paras2 = 0, pmode = -1;
DWORD slensize = 128, slen = 0;
V3MACRO *mbuftemp;
int bnest;
/* マクロ定義文字列抽出 */
lbuf = skipSpc2(lbuf);
lbuf_ = lbuf;
while (*lbuf != ' ' && *lbuf != '\t') { /* エラー処理いい加減 */
lbuf++;
}
len = lbuf - lbuf_;
/* マクロ定義リスト末尾に移動 */
while (mbuf->next != NULL) {
int mlen = strlen(mbuf->state1);
if (mlen == len) {
if(!stricmp2(mbuf->state1, lbuf_)) {
zmserror("same macro name is already used.",zmsdata->line,zmsdata->linebuf,lbuf,0,1);
}
break;
} else if (mlen < len) {
break;
}
mbuf = mbuf->next;
}
/* マクロ名定義 */
mbuftemp = (V3MACRO*)emalloc(sizeof(V3MACRO) * 1,"mbuftemp");
mbuftemp->state1 = (char*)emalloc(sizeof(char) * (len + 1),"mbuftemp->state1");
strncpy(mbuftemp->state1, lbuf_, len);
(mbuftemp->state1)[len] = '\0';
strlwr(mbuftemp->state1);
if (mbuf->next == NULL) { /* リスト最後尾追加 */
/* リスト最前列である可能性があるためすげ替えでなくコピーにしている */
mbuf->state1 = mbuftemp->state1;
efree(mbuftemp,"mbuftemp");
mbuf->next = (V3MACRO*)emalloc(sizeof(V3MACRO) * 1,"mbuf->next");
mbuf->next->next = NULL;
} else { /* リスト途中に挿入 */
UBYTE *state1 = mbuftemp->state1;
mbuftemp->state1 = mbuf->state1;
mbuftemp->state2 = mbuf->state2;
mbuftemp->paras = mbuf->paras;
mbuftemp->next = mbuf->next;
mbuf->next = mbuftemp;
mbuf->state1 = state1;
}
/* パラメータ記述法チェック % or %n */
lbuf = skipSpc2(lbuf);
while (*lbuf != '{') {
if (*lbuf++ == '%') {
lbuf = skipSpc3(lbuf, &zmsdata);
if (isdigit(*lbuf)) { /* %n pmode==1 */
if (!paras) { /* はじめの1個 */
pmode = 1;
} else if (!pmode) { /* % %n */
zmserror("macro parameter definition error.",zmsdata->line,zmsdata->linebuf,lbuf,0,1);
}
while (isdigit(*lbuf)) { /* 二桁以上の数字をスキップ */
lbuf++;
}
} else {
if (!paras) { /* はじめの1個 */
pmode = 0;
} else if (pmode) { /* %n % */
zmserror("macro parameter definition error.",zmsdata->line,zmsdata->linebuf,lbuf,0,1);
}
}
lbuf = skipSpc3(lbuf, &zmsdata);
if (*lbuf == ',') {
lbuf++;
}
paras++;
} else { /* それ以外はエラー */
zmserror("macro parameter definition error.",zmsdata->line,zmsdata->linebuf,lbuf,0,1);
}
lbuf = skipSpc2(lbuf);
}
mbuf->paras = paras;
/* コンテンツ記述開始チェック */
if (*lbuf != '{') {
zmserror("macro parameter definition error.",zmsdata->line,zmsdata->linebuf,lbuf,0,1);
}
lbuf = skipSpc3(lbuf, &zmsdata);
lbuf_ = lbuf;
/* コンテンツ記述チェック */
while (*lbuf != '}') {
if (*lbuf == 0x0d || *lbuf == 0x0a || !*lbuf) {
zmsdata = zmsdata->next;
ld = ld->next;
lbuf = zmsdata->linebuf;
if (zmsdata->next == NULL) {
zmserror("macro definition is not closed.",zmsdata->line,zmsdata->linebuf,lbuf,0,1);
}
continue;
}
if (*lbuf == '%') {
DWORD p;
int err;
lbuf = skipSpc3(lbuf + 1, &zmsdata);
lbuf = getnum2(lbuf, &p, &err);
if (err) { /* % */
if (pmode > 0) {
zmserror("macro contents mismatches(% and %n).",zmsdata->line,zmsdata->linebuf,lbuf,0,1);
}
} else { /* %n */
if (!pmode) {
zmserror("macro contents mismatches(% and %n).",zmsdata->line,zmsdata->linebuf,lbuf,0,1);
}
if (p > paras && paras) {
zmserror("too large the parameter# is.",zmsdata->line,zmsdata->linebuf,lbuf,0,1);
}
}
paras2++;
if (!pmode && paras2 > paras && paras) {
zmserror("too many parameters in macro contents.",zmsdata->line,zmsdata->linebuf,lbuf,0,1);
}
} else {
lbuf++;
}
}
if (!paras && paras2) {
mbuf->paras = paras2;
}
/* コンテンツのコピー */
lbuf = lbuf_ + 1; /* skip '{' */
zmsdata = zmsdata_;
mbuf->state2 = (UBYTE*)emalloc(sizeof(UBYTE) * slensize,"mbuf->state2");
bnest = 1;
while (bnest > 0) {
if (*lbuf == '{') { /* {..} nest check */
bnest++;
} else if (*lbuf == '}') {
if (--bnest <= 0) {
lbuf++;
break;
}
}
if (*lbuf == 0x0d || *lbuf == 0x0a || *lbuf == '/' || !*lbuf) {
zmsdata = zmsdata->next;
ld = ld->next;
lbuf = zmsdata->linebuf;
if (zmsdata->next == NULL) {
zmserror("macro definition is not closed.",zmsdata->line,zmsdata->linebuf,lbuf,0,1);
}
continue;
}
mbuf->state2[slen++] = *lbuf++;
if (slen >= slensize - 16) {
slensize += 128;
mbuf->state2 = (UBYTE*)erealloc(mbuf->state2, sizeof(UBYTE) * slensize,"mbuf->state2");
}
}
mbuf->state2[slen] = '\0';
/*
fprintf(stderr,"Defined: %s %d %s",mbuf->state1, mbuf->paras, mbuf->state2);
fprintf(stderr,"(%d)\n",strlen(mbuf->state2));
*/
*lbuf__ = lbuf;
return zmsdata;
}
/* マクロが使われていれば置換する */
/* まだBM法などのアルゴリズムは使っておらず、単純なもの */
void replaceMacro(ZMSDATA *zmsdata, V3MACRO *mbuf)
{
while (mbuf->next != NULL) {
char *lbuf = zmsdata->linebuf;
lbuf = skipSpc2(lbuf); /* .defineの定義名はマクロ展開しない */
if (!stricmp2(lbuf, ".define")) {
lbuf += 7; /* strlen(".define"); */
lbuf = skipSpc2(lbuf);
while (*lbuf != ' ' && *lbuf != '\t' && *lbuf != 0x0A && *lbuf) {
lbuf++;
}
}
while (*lbuf) {
if (*lbuf == '/') {
break; /* 注釈内はマクロ展開しない */
} else if (!stricmp2(lbuf,mbuf->state1)) {
char *lbuf_ = lbuf;
int i;
UBYTE *pbuf;
DWORD pbuflen = 0, pbuflenmax = 128;
V3MACROPARA *mpara, *mpara_;
char *state2 = mbuf->state2;
mpara_ = mpara = (V3MACROPARA*)emalloc(sizeof(V3MACROPARA) * 1,"mpara");
mpara->para = NULL;
mpara->next = NULL;
/*
fprintf(stderr,"macro is used! %s / %s / %s\n",mbuf->state1,lbuf,mbuf->state2);
*/
lbuf += strlen(mbuf->state1);
/* 展開パラメータ取得 */
for (i = 0; i < mbuf->paras; i++) {
lbuf = getpara2(lbuf,&(mpara->para));
/*
fprintf(stderr,"p: %s(%d) ",mpara->para,strlen(mpara->para));
*/
if (*lbuf == ',') {
lbuf++;
} else {
break;
}
mpara->next = (V3MACROPARA*)emalloc(sizeof(V3MACROPARA) * 1,"mpara->next");
mpara = mpara->next;
mpara->para = NULL;
mpara->next = NULL;
}
mpara = mpara_;
/*
fprintf(stderr,"![%s]",state2);
*/
/* 展開後MMLの作成 */
pbuf = (UBYTE*)emalloc(sizeof(UBYTE) * pbuflenmax,"pbuf");
while (*state2) {
if (*state2 != '%') { /* マクロパラメータでない場合 */
/* そのまま1文字コピー */
/*
fprintf(stderr,".");
*/
pbuf[pbuflen++] = *state2++;
if (pbuflen > pbuflenmax - 16) {
pbuflenmax += 128;
pbuf = (UBYTE*)erealloc(pbuf, sizeof(UBYTE) * pbuflenmax,"pbuf");
}
} else { /* マクロパラメータなら */
/* 対応するパラメータを埋める */
int err;
DWORD pno;
/*
fprintf(stderr,"%");
*/
state2 = getnum2(state2 + 1, &pno, &err);
if (!err) { /* 数字付パラメータなら */
int i;
mpara = mpara_;
for (i = 1; i < pno; i++) {
if (mpara->next) {
mpara = mpara->next;
} else {
break;
}
}
}
if (mpara->para != NULL) {
if (pbuflen + (DWORD)strlen(mpara->para) > pbuflenmax - 16) {
pbuflenmax += strlen(mpara->para) + 128;
pbuf = (UBYTE*)erealloc(pbuf, sizeof(UBYTE) * pbuflenmax,"pbuf");
}
/*
fprintf(stderr,"B[%s]",mpara->para);
*/
strcpy(&pbuf[pbuflen],mpara->para);
pbuflen += strlen(mpara->para);
pbuf[pbuflen] = '\0';
}
/*
fprintf(stderr,"C[%s](%d)",pbuf,strlen(mpara->para));
*/
mpara = mpara->next;
}
}
pbuf[pbuflen] = '\0';
/*
fprintf(stderr,"置換:%s\n",pbuf);
fprintf(stderr,"置換前:%s\n",zmsdata->linebuf);
*/
/* パラメータ領域の開放 */
do {
mpara = mpara_;
mpara_ = mpara_->next;
if (mpara->para != NULL) efree(mpara->para,"mbuf->para");
efree(mpara,"mpara");
} while (mpara_ != NULL);
/* 展開済みMMLを元のMMLと置換する */
/* lbuf, lbuf_, pbuf[plen], zmsdata->linebuf */
/* 転送バイト数: strlen(zmsdata->linebuf) - (lbuf - zmsdata->linebuf) */
/* 転送from: lbuf */
/* 転送to: lbuf + (pbuflen - (lbuf - lbuf_)) */
/* reallocするためポインタでなくオフセットを用いる */
{
DWORD transbytes = strlen(zmsdata->linebuf) - ((UBYTE*)lbuf - zmsdata->linebuf) + 1; /* +1: for \0 */
DWORD from = (UBYTE*)lbuf - zmsdata->linebuf;
DWORD to = (UBYTE*)lbuf - zmsdata->linebuf + (pbuflen - (lbuf - lbuf_ ));
DWORD copyfrom = (UBYTE*)lbuf_ - zmsdata->linebuf;
zmsdata->linebuf = erealloc(zmsdata->linebuf,
strlen(zmsdata->linebuf) - (lbuf - lbuf_) + pbuflen + 1,"linebuf");
memmove(zmsdata->linebuf + to,
zmsdata->linebuf + from,
transbytes);
memcpy(copyfrom + zmsdata->linebuf,pbuf,pbuflen);
lbuf = zmsdata->linebuf + to;
}
/*
fprintf(stderr,"置換後(%c%c%c):%s\n",*lbuf,*(lbuf+1),*(lbuf+2),zmsdata->linebuf);
*/
/*
lbuf += pbuflen;
*/
efree(pbuf,"pbuf");
} else { /* マクロと一致せず: 一文字ずらす */
lbuf++;
}
}
mbuf = mbuf->next;
}
}
/* 文字列対応パラメータ取得 */
UBYTE *getpara2(UBYTE *lbuf, char **para)
{
UBYTE *lbuf_;
DWORD plen;
lbuf_ = lbuf = skipSpc2(lbuf);
while (*lbuf != ',' && *lbuf != ' ' &&
*lbuf != '\t' && *lbuf != 0xFF &&
*lbuf != 0x0d && *lbuf != 0x0a && *lbuf != '/') {
lbuf++;
}
plen = lbuf - lbuf_;
*para = (UBYTE*)emalloc(sizeof(UBYTE) * (plen + 1),"para");
strncpy(*para,lbuf_,plen);
(*para)[plen] = '\0';
return lbuf;
}